﻿#pragma once

#include <learn/geom/geom_nonbasis.h>

#include <functional>

namespace xi
{

namespace geom
{

template <std::size_t N> class Geom_ParamSurface : public Geom_NonbasisSurface<N>
{
    using Vec = VecT<N, double>;

  public:
    Geom_ParamSurface(Geom_ParamSurface &&) = delete;

    /**
     * @brief 构造函数
     *
     * @param[in] first
     * @param[in] last
     * @param[in] f
     * @param[in] df
     * @param[in] ddf
     */
    Geom_ParamSurface(double u1, double u2, double v1, double v2, std::function<Vec(double, double)> f,
                      std::function<std::array<Vec, 2>(double, double)> df,
                      std::function<std::array<Vec, 3>(double, double)> ddf = nullptr)
        : m_u1(u1), m_u2(u2), m_v1(v1), m_v2(v2), m_f(f), m_df(df), m_ddf(ddf)
    {
    }

    /**
     * @brief 参数范围
     *
     * @return std::array<double, 4>
     */
    std::array<double, 4> bounds() const override
    {
        return {m_u1, m_u2, m_v1, m_v2};
    }

    /**
     * @brief 计算曲面上点的位置
     *
     * @param[in] u
     * @param[in] v
     * @return Vec
     */
    Vec d0(double u, double v) const override
    {
        return m_f(u, v);
    }

    /**
     * @brief 计算曲面上点的位置和导数
     *
     * @param[in] u
     * @param[in] v
     * @return std::array<Vec, 3>
     */
    std::array<Vec, 3> d1(double u, double v) const override
    {
        auto [du, dv] = m_df(u, v);
        return {m_f(u, v), du, dv};
    }

    /**
     * @brief 计算曲面上点的位置和二阶导数
     *
     * @param[in] u
     * @param[in] v
     * @return std::array<Vec, 6>
     */
    std::array<Vec, 6> d2(double u, double v) const override
    {
        auto [du, dv] = m_df(u, v);
        auto [d2u, d2v, d2uv] = m_ddf(u, v);
        return {m_f(u, v), du, dv, d2u, d2v, d2uv};
    }

    /**
     * @brief 序列化
     *
     * @return utils::Json
     */
    utils::Json dump_json() const override
    {
        utils::Json json;
        json["type"] = "param_surface";
        json["u1"] = m_u1;
        json["u2"] = m_u2;
        json["v1"] = m_v1;
        json["v2"] = m_v2;
        return json;
    }

  protected:
    double m_u1, m_u2, m_v1, m_v2;
    std::function<Vec(double, double)> m_f;
    std::function<std::array<Vec, 2>(double, double)> m_df;
    std::function<std::array<Vec, 3>(double, double)> m_ddf;
};

} // namespace geom

} // namespace xi
